using System;
using System.Collections.Generic;
using System.Data;
using System.Text;
using AIFSContentManagementSystem;
using AIFSContentManagementSystem.Articles;
using AIFSContentManagementSystem.Criteria;
using AIFSContentManagementSystem.Criteria.Messages;
using AIFSDataAccessLayer;
using AIFSDataAccessLayer.Exceptions;
using AIFSDataAccessLayer.Utilities;

namespace AIFSCMS.Infrastructure.Repositories
{
    using EntityMappers;
    using ProxyModels;

    public class ArticleRepository : IArticleRepository
    {
        IRepositoryMap repositoryMap;
        IDataExecutionContext dataExecutionContext;

        ICriteriaService criteriaService;

        public IRepositoryMap RepositoryMap
        {
            get
            {
                return repositoryMap;
            }
        }

        private IDataExecutionContext DataExecutionContext
        {
            get { return dataExecutionContext; }
        }

        public ArticleRepository(IRepositoryMap RepositoryMap, IDataExecutionContext DataExecutionContext, ICriteriaService CriteraService)
        {
            this.repositoryMap = RepositoryMap;
            this.criteriaService = CriteraService;
            this.dataExecutionContext = DataExecutionContext;
        }

        public List<Article> GetArticles(ArticleSearchParameter SearchParameter)
        {
            try
            {
                if (SearchParameter == null)
                {
                    throw new AIFSCMSInfrastructureException("Search parameter is required");
                }

                IDataExecutionContext dataContext = this.DataExecutionContext;
                DalSqlCommand cmd = dataContext.CreateStoredProcCommand("GetArticles");

                string articleType = TypeUtil.ValueOrNull(SearchParameter.ArticleType) as string;

                cmd.CreateParameter("ARTICLE_TYPE_CODE", articleType);
                cmd.CreateParameter("PROGRAM", SearchParameter.Program);
                cmd.CreateParameter("ACTIVE_FLAG", SearchParameter.ActiveFlag);

                DataTable dt = dataContext.ExecuteDataTable(cmd);

                ArticleMapper mapper = new ArticleMapper();

                List<ArticleProxy> proxies = mapper.Map(dt);

                //Return as List of Article
                List<Article> articles = new List<Article>();
                foreach (ArticleProxy proxy in proxies)
                {
                    proxy.RepositoryMap = this.repositoryMap;
                    if (this.RepositoryMap.DisableLazyLoad && proxy is IProxy)
                    {
                        proxy.EagerLoad();
                    }

                    articles.Add(proxy);
                }

                return articles;
            }
            catch (AIFSCMSInfrastructureException rex)
            {
                throw rex;
            }
            catch (UserDBException udbex)
            {
                throw new AIFSCMSInfrastructureException(udbex.Message, udbex);
            }
            catch (AIFSDataAccessLayerException dalex)
            {
                throw new AIFSCMSInfrastructureException("Could not retrieve article list", dalex);
            }
            catch (Exception ex)
            {
                throw new AIFSCMSInfrastructureException("System error occured when retrieving article list", ex);
            }
        }

        public Article GetArticle(int? ArticleId)
        {
            try
            {
                if (TypeUtil.ValueOrNull(ArticleId) == null)
                {
                    throw new AIFSCMSInfrastructureException("Article Id is required to get the article.");
                }

                DalSqlCommand cmd = this.DataExecutionContext.CreateStoredProcCommand("GetArticle");

                cmd.CreateParameter("ARTICLE_ID", ArticleId);

                DataTable dt = this.DataExecutionContext.ExecuteDataTable(cmd);

                ArticleMapper mapper = new ArticleMapper();
                ArticleProxy proxy = mapper.MapSingleRow(dt);

                if (proxy == null)
                    return null;

                proxy.RepositoryMap = this.RepositoryMap;

                if (this.RepositoryMap.DisableLazyLoad)
                {
                    proxy.EagerLoad();
                }

                return proxy;
            }
            catch (AIFSCMSInfrastructureException rex)
            {
                throw rex;
            }
            catch (UserDBException udbex)
            {
                throw new AIFSCMSInfrastructureException(udbex.Message, udbex);
            }
            catch (AIFSDataAccessLayerException dalex)
            {
                throw new AIFSCMSInfrastructureException("Could not retrieve article", dalex);
            }
            catch (Exception ex)
            {
                throw new AIFSCMSInfrastructureException("System error occured when retrieving article", ex);
            }
        }

        public List<ArticleCriterion> GetArticleCriteria(Article Article)
        {
            try
            {
                DalSqlCommand cmd = this.DataExecutionContext.CreateStoredProcCommand("GetCriteriaForArticle");

                cmd.CreateParameter("ARTICLE_ID", Article.ArticleId);

                DataTable dt = this.DataExecutionContext.ExecuteDataTable(cmd);

                ArticleCriteriaMapper mapper = new ArticleCriteriaMapper();
                List<ArticleCriterion> articleCriteria = mapper.Map(dt);
                return articleCriteria;
            }
            catch (AIFSCMSInfrastructureException rex)
            {
                throw rex;
            }
            catch (UserDBException udbex)
            {
                throw new AIFSCMSInfrastructureException(udbex.Message, udbex);
            }
            catch (AIFSDataAccessLayerException dalex)
            {
                throw new AIFSCMSInfrastructureException("Could not retrieve criteria for the article", dalex);
            }
            catch (Exception ex)
            {
                throw new AIFSCMSInfrastructureException("System error occured when retrieving criteria for the article", ex);
            }
        }

        public ArticleCriterion GetArticleCriterion(Article Article, Criterion Criterion)
        {
            try
            {
                DalSqlCommand cmd = this.DataExecutionContext.CreateStoredProcCommand("GetArticleCriterion");

                cmd.CreateParameter("ARTICLE_ID", Article.ArticleId);
                cmd.CreateParameter("CRITERIA_ID", Criterion.CriteriaId);

                DataTable dt = this.DataExecutionContext.ExecuteDataTable(cmd);
                if (dt == null || dt.Rows.Count == 0)
                {
                    return null;
                }

                ArticleCriteriaMapper mapper = new ArticleCriteriaMapper();
                ArticleCriterion articleCriterion = mapper.MapSingleRow(dt);
                return articleCriterion;
            }
            catch (AIFSCMSInfrastructureException rex)
            {
                throw rex;
            }
            catch (UserDBException udbex)
            {
                throw new AIFSCMSInfrastructureException(udbex.Message, udbex);
            }
            catch (AIFSDataAccessLayerException dalex)
            {
                throw new AIFSCMSInfrastructureException("Could not retrieve criteria for the article", dalex);
            }
            catch (Exception ex)
            {
                throw new AIFSCMSInfrastructureException("System error occured when retrieving criteria for the article", ex);
            }
        }

        public List<ArticleFragment> GetArticleFragments(Article Article)
        {
            try
            {
                if (Article == null || TypeUtil.ValueOrNull(Article.ArticleId) == null)
                {
                    throw new AIFSCMSInfrastructureException("Article Id is required to get fragments");
                }

                DalSqlCommand cmd = this.DataExecutionContext.CreateStoredProcCommand("GetFragmentsForArticle");

                cmd.CreateParameter("ARTICLE_ID", Article.ArticleId);

                DataTable dt = this.DataExecutionContext.ExecuteDataTable(cmd);
                ArticleFragmentMapper mapper = new ArticleFragmentMapper();
                List<ArticleFragment> articleFragments = mapper.Map(dt);
                return articleFragments;
            }
            catch (AIFSCMSInfrastructureException rex)
            {
                throw rex;
            }
            catch (UserDBException udbex)
            {
                throw new AIFSCMSInfrastructureException(udbex.Message, udbex);
            }
            catch (AIFSDataAccessLayerException dalex)
            {
                throw new AIFSCMSInfrastructureException("Could not retrieve fragments of the article", dalex);
            }
            catch (Exception ex)
            {
                throw new AIFSCMSInfrastructureException("System error occured when retrieving fragments of the article", ex);
            }
        }

        private ArticleFragment GetArticleFragment(Article Article, Fragment Fragment)
        {
            try
            {
                if (Article == null || Fragment == null ||
                    TypeUtil.ValueOrNull(Article.ArticleId) == null || TypeUtil.ValueOrNull(Fragment.FragmentId) == null)
                {
                    throw new AIFSCMSInfrastructureException("Article and Fragment are required to get the fragment link");
                }

                DalSqlCommand cmd = this.DataExecutionContext.CreateStoredProcCommand("GetArticleFragment");

                cmd.CreateParameter("ARTICLE_ID", Article.ArticleId);
                cmd.CreateParameter("FRAGMENT_ID", Fragment.FragmentId);

                DataTable dt = this.DataExecutionContext.ExecuteDataTable(cmd);
                if (dt == null || dt.Rows.Count == 0)
                {
                    return null;
                }

                ArticleFragmentMapper mapper = new ArticleFragmentMapper();
                ArticleFragment articleFragment = mapper.MapSingleRow(dt);
                return articleFragment;
            }
            catch (AIFSCMSInfrastructureException rex)
            {
                throw rex;
            }
            catch (UserDBException udbex)
            {
                throw new AIFSCMSInfrastructureException(udbex.Message, udbex);
            }
            catch (AIFSDataAccessLayerException dalex)
            {
                throw new AIFSCMSInfrastructureException("Could not retrieve fragments of the article", dalex);
            }
            catch (Exception ex)
            {
                throw new AIFSCMSInfrastructureException("System error occured when retrieving fragments of the article", ex);
            }
        }

        public Fragment GetFragment(int? FragmentId)
        {
            try
            {
                if (TypeUtil.ValueOrNull(FragmentId) == null)
                {
                    throw new AIFSCMSInfrastructureException("Fragment Id is required to retrieve a fragment");
                }

                DalSqlCommand cmd = this.DataExecutionContext.CreateStoredProcCommand("GetFragment");

                cmd.CreateParameter("FRAGMENT_ID", FragmentId);

                DataTable dt = this.DataExecutionContext.ExecuteDataTable(cmd);

                FragmentMapper mapper = new FragmentMapper();

                Fragment fragment = mapper.MapSingleRow(dt);
                return fragment;
            }
            catch (AIFSCMSInfrastructureException rex)
            {
                throw rex;
            }
            catch (UserDBException udbex)
            {
                throw new AIFSCMSInfrastructureException(udbex.Message, udbex);
            }
            catch (AIFSDataAccessLayerException dalex)
            {
                throw new AIFSCMSInfrastructureException("Could not retrieve fragments of the article", dalex);
            }
            catch (Exception ex)
            {
                throw new AIFSCMSInfrastructureException("System error occured when retrieving fragments of the article ", ex);
            }
        }

        public void Save(Article Article)
        {
            try
            {
                if (Article == null)
                {
                    throw new AIFSCMSInfrastructureException("Article is empty. Nothing to save.");
                }

                DalSqlCommand cmd = this.DataExecutionContext.CreateStoredProcCommand("SaveArticle");
                cmd.CreateParameter("ARTICLE_ID", Article.ArticleId);
                cmd.CreateParameter("ARTICLE_TYPE_CODE", Article.ArticleType);
                cmd.CreateParameter("PROGRAM", Article.Program);
                cmd.CreateParameter("EFFECTIVE_FROM", Article.EffectiveFrom);
                cmd.CreateParameter("EFFECTIVE_THRU", Article.EffectiveThru);
                cmd.CreateParameter("ACTIVE_FLAG", Article.Active);
                cmd.CreateParameter("LAST_TIMESTAMP", Article.LastTimestamp);
                cmd.CreateParameter("USER_ID", Article.LastUserId);

                Article.ArticleId = int.Parse(this.DataExecutionContext.ExecuteScalar(cmd).ToString());


                foreach (Fragment fragment in Article.Fragments)
                {
                    if (string.IsNullOrEmpty(fragment.LastUserId))
                        fragment.LastUserId = Article.LastUserId;

                    this.Save(fragment);

                    //Check if link between this article and the fragment exists
                    ArticleFragment articleFragment = this.GetArticleFragment(Article, fragment);
                    if (articleFragment == null) //does not exist. create the link
                    {
                        articleFragment = new ArticleFragment();
                        articleFragment.ArticleId = Article.ArticleId;
                        articleFragment.FragmentId = fragment.FragmentId;
                        articleFragment.LastTimestamp = DateTime.Now;
                        articleFragment.LastUserId = Article.LastUserId;

                        this.Save(articleFragment);
                    }
                }

                //Save the criteria for this article and link to the article
                foreach (Criterion criterion in Article.Criteria)
                {
                    if (string.IsNullOrEmpty(criterion.LastUserId))
                        criterion.LastUserId = Article.LastUserId;

                    SaveCriterionRequest saveCriterionReq = new SaveCriterionRequest();
                    saveCriterionReq.Criterion = criterion;
                    saveCriterionReq.UserId = criterion.LastUserId;

                    SaveCriterionResponse saveCriterionRes = criteriaService.Process(saveCriterionReq);

                    //If link does not exists create it
                    ArticleCriterion articleCriterion = this.GetArticleCriterion(Article, saveCriterionRes.Criterion);
                    if (articleCriterion == null) //create the link
                    {
                        articleCriterion = new ArticleCriterion();
                        articleCriterion.ArticleId = Article.ArticleId;
                        articleCriterion.CriteriaId = saveCriterionRes.Criterion.CriteriaId;
                        articleCriterion.LastTimestamp = Article.LastTimestamp;
                        articleCriterion.LastUserId = Article.LastUserId;

                        this.Save(articleCriterion);
                    }
                }
            }
            catch (AIFSCMSInfrastructureException rex)
            {
                throw rex;
            }
            catch (UserDBException udbex)
            {
                throw new AIFSCMSInfrastructureException(udbex.Message, udbex);
            }
            catch (AIFSDataAccessLayerException dalex)
            {
                throw new AIFSCMSInfrastructureException("Could not save the article", dalex);
            }
            catch (Exception ex)
            {
                throw new AIFSCMSInfrastructureException("System error occured when saving the article", ex);
            }
        }

        private void Save(Fragment Fragment)
        {
            try
            {
                if (Fragment == null)
                {
                    throw new AIFSCMSInfrastructureException("Fragment is empty. Can not be saved");
                }

                DalSqlCommand cmd = this.DataExecutionContext.CreateStoredProcCommand("SaveFragment");
                cmd.CreateParameter("FRAGMENT_ID", Fragment.FragmentId);
                cmd.CreateParameter("HEADER_TEXT", Fragment.HeaderText);
                cmd.CreateParameter("HTML_BODY", Fragment.HtmlBody);
                if (Fragment.Image != null)
                {
                    cmd.CreateParameter("IMAGE_ALT_TEXT", Fragment.Image.AltText);
                    cmd.CreateParameter("IMAGE_URL", Fragment.Image.Url);
                }
                else
                {
                    cmd.CreateNullParameter("IMAGE_ALT_TEXT");
                    cmd.CreateNullParameter("IMAGE_URL");
                }

                cmd.CreateParameter("LAST_TIMESTAMP", Fragment.LastTimestamp);
                cmd.CreateParameter("USER_ID", Fragment.LastUserId);

                Fragment.FragmentId = int.Parse(this.DataExecutionContext.ExecuteScalar(cmd).ToString());
            }
            catch (AIFSCMSInfrastructureException rex)
            {
                throw rex;
            }
            catch (UserDBException udbex)
            {
                throw new AIFSCMSInfrastructureException(udbex.Message, udbex);
            }
            catch (AIFSDataAccessLayerException dalex)
            {
                throw new AIFSCMSInfrastructureException("Could not save the fragment", dalex);
            }
            catch (Exception ex)
            {
                throw new AIFSCMSInfrastructureException("System error occured when saving the fragment", ex);
            }
        }

        private void Save(ArticleFragment ArticleFragment)
        {
            try
            {
                if (ArticleFragment == null)
                {
                    throw new AIFSCMSInfrastructureException("Article fragment is empty. Can not be saved");
                }

                DalSqlCommand cmd = this.DataExecutionContext.CreateStoredProcCommand("SaveArticleFragment");
                cmd.CreateParameter("FRAGMENT_ID", ArticleFragment.FragmentId);
                cmd.CreateParameter("ARTICLE_ID", ArticleFragment.ArticleId);
                cmd.CreateParameter("USER_ID", ArticleFragment.LastUserId);

                ArticleFragment.ArticleFragmentId = int.Parse(this.DataExecutionContext.ExecuteScalar(cmd).ToString());
            }
            catch (AIFSCMSInfrastructureException rex)
            {
                throw rex;
            }
            catch (UserDBException udbex)
            {
                throw new AIFSCMSInfrastructureException(udbex.Message, udbex);
            }
            catch (AIFSDataAccessLayerException dalex)
            {
                throw new AIFSCMSInfrastructureException("Could not link fragment to the article", dalex);
            }
            catch (Exception ex)
            {
                throw new AIFSCMSInfrastructureException("System error occured when linking fragment to the article", ex);
            }
        }

        private void Save(ArticleCriterion ArticleCriterion)
        {
            try
            {
                if (ArticleCriterion == null)
                {
                    throw new AIFSCMSInfrastructureException("Article criterion is empty. Can not be saved");
                }

                DalSqlCommand cmd = this.DataExecutionContext.CreateStoredProcCommand("SaveArticleCriterion");
                cmd.CreateParameter("CRITERIA_ID", ArticleCriterion.CriteriaId);
                cmd.CreateParameter("ARTICLE_ID", ArticleCriterion.ArticleId);
                cmd.CreateParameter("USER_ID", ArticleCriterion.LastUserId);

                ArticleCriterion.ArticleCriteriaId = int.Parse(this.DataExecutionContext.ExecuteScalar(cmd).ToString());
            }
            catch (AIFSCMSInfrastructureException rex)
            {
                throw rex;
            }
            catch (UserDBException udbex)
            {
                throw new AIFSCMSInfrastructureException(udbex.Message, udbex);
            }
            catch (AIFSDataAccessLayerException dalex)
            {
                throw new AIFSCMSInfrastructureException("Could not link criterion to the article", dalex);
            }
            catch (Exception ex)
            {
                throw new AIFSCMSInfrastructureException("System error occured when linking criterion to the article", ex);
            }
        }
    }
}
